// Copyright 2011 Jay Young. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview
 *
 * The JSGI bootstrap script.  The application should be a package in the
 * "packages" folder.  Request paths are referenced in the "./urls" module,
 * the matching result being the module ID of the request handler.  We try to
 * load that module, and then we pass the Mapping's object to that module's
 * JSGI application function with the same name as the HTTP Method used to
 * make the request.
 *
 * For example, a "POST" request to "/app/get/post-1" may find "app/get" as
 * a module ID in the require("./urls").urls, and will call
 * "require("app/get").post(mapping);"
 */


var {urls} = require('./urls'),
    logger = require('ringo/logging').getLogger('main');


/**
 * An object with three properties:
 *   - pathPrefix: The first part of the URL, mapped in the servlet mapping.
 *      This does not show in req.pathInfo.  `pathPrefix` should always begin
 *      but never end in a slash.
 *   - module: The most relevant module mapping from the URL map.
 *   - path: The part of the req.pathInfo that was matched in the URL map.
 *      `path` should always begin but never end in a slash
 *   - params: Everything after `path` in req.pathInfo.  Will not start or end
 *             with a slash
 *   - req: The JsgiRequest
 *
 * @typedef {{pathPrefix: string, module: string, path: string, params: string,
 *            req: JsgiRequest}}
 */
var Mapping;


/**
 * This is the path matched by th servlet mapping in web.xml.  It does not
 * appear in req.pathInfo, so it is defined here as a constant and included in
 * Mapping objects in case request handlers have to build full paths for
 * redirects and such.
 *
 * @const
 * @type {string}
 */
var pathPrefix = '/api';


/**
 * Disect the request to determine which module to load.  Handler modules must
 * export a function for every HTTP method they expect to handle, normalied to
 * lower case (GET => 'get', POST => 'post', etc).  These functions must take
 * one argument:  The Mapping object returned by getMapping.
 *
 * @param {JsgiRequest} req The request.
 * @return {JsgiResponse} The response object.
 */
exports.app = function(req) {
  var path = req.pathInfo,
      mapping = getMapping(path, req),
      method = req.method.toLowerCase();

  try {
    var module = require(mapping.module);
  }
  catch (e) {
    logger.error('Error loading module: ' + mapping.module);
    logger.error(e);
    return {
      status: 500,
      headers: {'Content-Type': 'text/plain'},
      body: ['Error loading module: ', mapping.module]
    };
  }

  try {
    return module[method](mapping);
  }
  catch (e) {
    logger.error('Something went wrong: ', e.toString());
    return {
      status: 500,
      headers: {'Content-Type': 'text/plain'},
      body: [
        'Error executing handler for path: ', mapping.path, '\n',
        e.toString()
      ]
    };
  }
};


/**
 * Take request.pathInfo and figure out the most relevant mapping in the
 * URL map.
 *
 * @param {string} path The full request path.
 * @param {JsgiRequest} req The request object.
 * @return {{Mapping}} The mapping for this request.
 */
var getMapping = function(path, req) {
  var params = '',
      pathIndex = path.length - 1;

  // Check if the whole of path is a path handled in urlMap
  var mapping = urls[path];
  while ((! mapping) && pathIndex > 0) {
    mapping = urls[path];
    if (! mapping) {
      // If no handler was found at the full path, start chopping off pieces
      // at each '/'.
      pathIndex = path.lastIndexOf('/');
      if (pathIndex > -1) {
        // Anything that's not part of the path is a parameter to be passed
        // to the handler.
        params = params === '' ?
            path.substring(pathIndex + 1) :
            [path.substring(pathIndex + 1), params].join('/');
        path = path.substring(0, pathIndex);
      }
    }
  }

  return {
    pathPrefix: pathPrefix,
    module: mapping,
    path: !!mapping ? path : '',
    params: !!mapping ? params : '',
    req: req
  };
};
